7-11 畅通工程之局部最小花费问题(最小生成树+并查集)

题目链接
某地区经过对城镇交通状况的调查,得到现有城镇间快速道路的统计数据,并提出“畅通工程”的目标:使整个地区任何两个城镇间都可以实现快速交通(但不一定有直接的快速道路相连,只要互相间接通过快速路可达即可)。现得到城镇道路统计表,表中列出了任意两城镇间修建快速路的费用,以及该道路是否已经修通的状态。现请你编写程序,计算出全地区畅通需要的最低成本。

输入格式:

输入的第一行给出村庄数目N (1≤N≤100);随后的N(N−1)/2行对应村庄间道路的成本及修建状态:每行给出4个正整数,分别是两个村庄的编号(从1编号到N),此两村庄间道路的成本,以及修建状态 — 1表示已建,0表示未建。

输出格式:

输出全省畅通需要的最低成本。

输入样例:

4
1 2 1 1
1 3 4 0
1 4 1 1
2 3 3 0
2 4 2 1
3 4 5 0

输出样例:

3

第一种:结构体加并查集

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef struct Node{
    int mt1;
    int mt2;
    int fee;
    int sense;
}Node;
Node e[10001];
int f[101];
bool cmp(Node a,Node b){
    return a.fee < b.fee;
}
void init(int n){
    for(int i = 1;i <= n;i++)
        f[i] = i;
    return;
}
int getf(int x){
    if(f[x] == x)
        return x;
    f[x] = getf(f[x]);
    return f[x];
}
int merge(int a,int b){
    int tx = getf(a);
    int ty = getf(b);
    if(tx != ty){
        f[ty] = tx;
        return 1;
    }
    return 0;
}
int main(){
    int n, sm = 0,cnt = 0;
    scanf("%d",&n);
    int m = n * (n - 1) / 2;
    for(int i = 0;i < m;i++)
        scanf("%d%d%d%d",&e[i].mt1,&e[i].mt2,&e[i].fee,&e[i].sense);
    sort(e,e + m,cmp);//默认权值从小到大对边进行排序
    init(n);//初始化
    //优先访问已经修建过的,因为已经修建过的花费的费用为0!!!!!前者之鉴,后事之师    
    for(int i = 0;i < m;i++)
        if(e[i].sense==1)
          merge(e[i].mt1,e[i].mt2)==1 ? cnt++ : cnt;
    for(int i = 0; i< m;i++){//从小到大枚举每一条边
        if(merge(e[i].mt1,e[i].mt2)){
            if(!e[i].sense)
                sm += e[i].fee;
            cnt++;//计数器
        }
        if(cnt == n - 1)//知道选用了 n - 1条边
            break;
    }
    printf("%d\n",sm);
    return 0;
}

第二种优先队列加并查集

#include<bits/stdc++.h>
using namespace std;
struct Node{
	long long v1,v2,fee;
	bool friend operator < (Node a,Node b){
		return a.fee > b.fee;
	}
	Node(int vv1 = 0,int vv2 = 0,int f = 0) : v1(vv1),v2(vv2),fee(f){}
}p;
int n,a,b,c,d,k,sum,f[105];
priority_queue <Node> q;	
int getf(int x){
	return x == f[x] ? x : f[x] = getf(f[x]); 
}
int merge(int x,int y){
	int tx = getf(x);
	int ty = getf(y);
	if(tx != ty){
		f[ty] = tx;
		return 0;
	}
	return 1;
}
int main(){		
	cin >> n;
	for(int i = 1;i <= n;i++){
		f[i] = i;
	}
	for(int i = 0;i < n * (n - 1) / 2;i++){
		cin >> a >> b >> c >> d;
		if(d == 1){
			merge(a,b);
		}
		else{
			q.push(Node(a,b,c));
		}
	}
	sum = 0;
	while(!q.empty()){
		p = q.top();	
		q.pop();	
		if(merge(p.v1,p.v2) == 1){//!!!如果p.v1和p.v2已经通过别的点间接连接,那么就不需要在连接!!!
			continue;
		}
		sum += p.fee;
		k = 0;
		for(int i = 1;i <= n;i++){
			if(f[i] == i){
				k++;
			}
		}	
		if(k == 1){
			cout << sum << endl;
			return 0;
		}		
	}
	cout << 0 << endl;
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值